#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <errno.h>

#include "network.h"
#include "list.h"
#include "CommonUtill.h"
#include "Log.h"
#include "stringlib.h"
#include "../CharLength.h"
#include "../connect/MediaConnect.h"
#include "storage_api.h"
#include "VideoRecorder.h"

#define OUTPUT_VIDEO_DATA_TO_FILE              1
#define OUTPUT_VIDEO_DATA_FILE_PATH            "/mnt/media/DemoRecord.h264"

#define MEDIA_API_VIDEO_DATA_SUBSCRIBE                 "/sos/v1/media/video/data/subscribe"
#define MEDIA_API_VIDEO_DATA_SUBSCRIBE_CANCEL          "/sos/v1/media/video/data/unsubscribe"
#define MEDIA_API_CONFIG_VIDEO_STREAM                  "/sos/v1/media/config/video/streamParam"
#define MEDIA_API_GET_VIDEO_STREAM_CONFIG              "/sos/v1/media/get/video/stream/config"
#define MEDIA_API_VIDEO_REFRESH_IFRAME                 "/sos/v1/media/video/refresh/IFrame"

#define MEDIA_ACTION_SUBSCRIBE_VIDEO_DATA              "subscribeVideoData"
#define MEDIA_ACTION_CONFIG_VIDEO_STREAM               "configVideoStream"
#define MEDIA_ACTION_GET_VIDEO_STREAM_CONFIG           "getVideoStreamConfig"
#define MEDIA_ACTION_REFRESH_IFRAME                    "refreshIframe"

typedef struct{
    char streamType[16];
    int definition;
    int bitRate;
    int frameRate;
}VideoConfigParam;

typedef struct{
    FILE *recordFile;
    bctbx_list_t *taskList;
    pthread_mutex_t mutex;
}VideoRecordManager;

static void subscribeVideoDataRequest(const char *authCode, VideoSubscribeTask *task);
static void configVideoStreamRequest(const char *authCode, VideoConfigParam *configParam);


static VideoRecordManager videoManager;

static VideoSubscribeTask *findVideoTaskByTag(const char *tag){
    if(is_empty_str(tag)){
        return NULL;
    }
    bctbx_list_t *elem = videoManager.taskList;
    VideoSubscribeTask *temp = NULL;
    for (; NULL != elem; elem = elem->next){
        temp = (VideoSubscribeTask *)elem->data;
        if(NULL == temp){
            continue;
        }
        if(0 == strcmp(tag, temp->tag)){
            return temp;
        }
    }
    return NULL;
}

VideoSubscribeTask *video_record_find_task_by_code(const char *taskCode){
    if(is_empty_str(taskCode)){
        return NULL;
    }
    bctbx_list_t *elem = videoManager.taskList;
    VideoSubscribeTask *temp = NULL;
    for (; NULL != elem; elem = elem->next){
        temp = (VideoSubscribeTask *)elem->data;
        if(NULL == temp){
            continue;
        }
        if(0 == strcmp(taskCode, temp->taskCode)){
            return temp;
        }
    }
    return NULL;
}

//==============================认证、建立相机视频数据订阅通道begin============================

static void mediaAuthCodeCallback(void *userData, const char *authCode){
    if(NULL == userData){
        return;
    }
    MsgObject *msg = (MsgObject *)userData;
    log_d("after finish auth request, do action for %s", msg->strMsg);
    if(0 == strcmp(MEDIA_ACTION_SUBSCRIBE_VIDEO_DATA, msg->strMsg)){
        subscribeVideoDataRequest(authCode, (VideoSubscribeTask *)msg->msg);
    }else if(0 == strcmp(MEDIA_ACTION_CONFIG_VIDEO_STREAM, msg->strMsg)){
        configVideoStreamRequest(authCode, (VideoConfigParam *)msg->msg);
    }else if(0 == strcmp(MEDIA_ACTION_GET_VIDEO_STREAM_CONFIG, msg->strMsg)){
        
    }
}

static void subscribeVideoDataCallback(sos_nw_rsp_t *rspBody, void *userData){
    int status = -1;
    cJSON *bodyJson = isRequestSuccess(rspBody, userData, &status, "subscribeVideoData");
    if (bodyJson == NULL || status != 0){
        //认证码不合格，刷新认证码
        if(10004 == status){
            MsgObject *msg = (MsgObject *)sos_malloc(sizeof(MsgObject), "MsgObject");
            msg->msg = userData;
            memset(msg->strMsg, 0, sizeof(msg->strMsg));
            snprintf(msg->strMsg, sizeof(msg->strMsg), "%s", MEDIA_ACTION_SUBSCRIBE_VIDEO_DATA);
            media_refresh_auth_code(msg, mediaAuthCodeCallback);
        }
        return;
    }
    cJSON *taskCodeJson = cJSON_GetObjectItemCaseSensitive(bodyJson, "TaskCode");
    if (cJSON_IsString(taskCodeJson) && !is_empty_str(taskCodeJson->valuestring)) {
    }else{
        log_e("subscribeVideoData failed !!!");
        return;
    }
    VideoSubscribeTask *task = (VideoSubscribeTask *) userData;
    if(NULL == task){
        log_e("subscribeVideoData gennerate task failed !!!");
        return;
    }
    memset(task->taskCode, 0, sizeof(task->taskCode));
    memset(task->wsUrl, 0, sizeof(task->wsUrl));
    task->wsPort = 0;
    task->state = 0;
    snprintf(task->taskCode, sizeof(task->taskCode), "%s", taskCodeJson->valuestring);
    cJSON *wsUrlJson = cJSON_GetObjectItemCaseSensitive(bodyJson, "WsUrl");
    if (cJSON_IsString(wsUrlJson) && !is_empty_str(wsUrlJson->valuestring)) {
        snprintf(task->wsUrl, sizeof(task->wsUrl), "%s", wsUrlJson->valuestring);
    }
    cJSON *wsPortJson = cJSON_GetObjectItemCaseSensitive(bodyJson, "WsPort");
    if (cJSON_IsNumber(wsPortJson)) {
        task->wsPort = wsPortJson->valueint;
    }
    pthread_mutex_lock(&videoManager.mutex);
    
    pthread_mutex_unlock(&videoManager.mutex);
    cJSON_Delete(bodyJson);

    //根据返回信息建立WS连接，及视频服务的数据通道
    media_build_data_channel(task->taskCode, task->wsUrl, task->wsPort, task->dataCallback);
}

static void subscribeVideoDataRequest(const char *authCode, VideoSubscribeTask *task){
    cJSON *obj = cJSON_CreateObject();
    cJSON_AddItemToObject(obj, "AuthCode", cJSON_CreateString(authCode));
    cJSON_AddItemToObject(obj, "StreamType", cJSON_CreateString(task->streamType));
    char *requestBody = cJSON_PrintUnformatted(obj);
    log_i("subscribeVideoData request body msg content = %s", requestBody);

    int result = sos_NwHttpSendRequest(MEDIA_HTTP_SERVER_LOCAL_ADDRESS, 
    MEDIA_HTTP_SERVER_LISTEN_PORT, MEDIA_API_VIDEO_DATA_SUBSCRIBE, NW_HTTP_METHOD_POST, 
    requestBody, NW_DEFAULT_TOKEN, task, 0, subscribeVideoDataCallback);
    sos_free(requestBody);
    cJSON_Delete(obj);
}

static void cancelSubscribeVideoDataCallback(sos_nw_rsp_t *rspBody, void *userData){
    int status = -1;
    cJSON *bodyJson = isRequestSuccess(rspBody, userData, &status, "cancelSubscribeVideoData");
    if (bodyJson == NULL || status != 0){
        return;
    }
    log_d("cancelSubscribeVideoData success");
    cJSON_Delete(bodyJson);
}

static void cancelSubscribeVideoDataRequest(const char *authCode, const char *taskCode){
    cJSON *obj = cJSON_CreateObject();
    cJSON_AddItemToObject(obj, "AuthCode", cJSON_CreateString(authCode));
    cJSON_AddItemToObject(obj, "TaskCode", cJSON_CreateString(taskCode));
    char *requestBody = cJSON_PrintUnformatted(obj);
    log_i("cancelSubscribeVideoDataRequest request body msg content = %s", requestBody);

    int result = sos_NwHttpSendRequest(MEDIA_HTTP_SERVER_LOCAL_ADDRESS, 
    MEDIA_HTTP_SERVER_LISTEN_PORT, MEDIA_API_VIDEO_DATA_SUBSCRIBE_CANCEL, NW_HTTP_METHOD_POST, 
    requestBody, NW_DEFAULT_TOKEN, NULL, 0, cancelSubscribeVideoDataCallback);
    sos_free(requestBody);
    cJSON_Delete(obj);
}

static void configVideoStreamCallback(sos_nw_rsp_t *rspBody, void *userData){
    int status = -1;
    cJSON *bodyJson = isRequestSuccess(rspBody, userData, &status, "configVideoStream");
    if (bodyJson == NULL || status != 0){
        //刷新认证码
        if(10004 == status){
            MsgObject *msg = (MsgObject *)sos_malloc(sizeof(MsgObject), "MsgObject");
            msg->msg = userData;
            memset(msg->strMsg, 0, sizeof(msg->strMsg));
            snprintf(msg->strMsg, sizeof(msg->strMsg), "%s", MEDIA_ACTION_CONFIG_VIDEO_STREAM);
            media_refresh_auth_code(msg, mediaAuthCodeCallback);
        }
        return;
    }
    log_d("configVideoStreamCallback success");
    cJSON_Delete(bodyJson);
    sos_free(userData);
}

static void configVideoStreamRequest(const char *authCode, VideoConfigParam *configParam){
    cJSON *obj = cJSON_CreateObject();
    cJSON_AddItemToObject(obj, "AuthCode", cJSON_CreateString(authCode));
    cJSON_AddItemToObject(obj, "Definition", cJSON_CreateNumber(configParam->definition));
    cJSON_AddItemToObject(obj, "BitRate", cJSON_CreateNumber(configParam->bitRate));
    cJSON_AddItemToObject(obj, "FrameRate", cJSON_CreateNumber(configParam->frameRate));
    cJSON_AddItemToObject(obj, "StreamType", cJSON_CreateString(configParam->streamType));
    char *requestBody = cJSON_PrintUnformatted(obj);
    log_i("configVideoStreamRequest request body msg content = %s", requestBody);

    int result = sos_NwHttpSendRequest(MEDIA_HTTP_SERVER_LOCAL_ADDRESS, 
    MEDIA_HTTP_SERVER_LISTEN_PORT, MEDIA_API_CONFIG_VIDEO_STREAM, NW_HTTP_METHOD_POST, 
    requestBody, NW_DEFAULT_TOKEN, configParam, 0, configVideoStreamCallback);
    sos_free(requestBody);
    cJSON_Delete(obj);
}

static void refreshIframeCallback(sos_nw_rsp_t *rspBody, void *userData){
    int status = -1;
    cJSON *bodyJson = isRequestSuccess(rspBody, userData, &status, "refreshIframe");
    if (bodyJson == NULL || status != 0){
        //刷新认证码
        if(10004 == status){
            MsgObject *msg = (MsgObject *)sos_malloc(sizeof(MsgObject), "MsgObject");
            msg->msg = userData;
            memset(msg->strMsg, 0, sizeof(msg->strMsg));
            snprintf(msg->strMsg, sizeof(msg->strMsg), "%s", MEDIA_ACTION_CONFIG_VIDEO_STREAM);
            media_refresh_auth_code(msg, mediaAuthCodeCallback);
        }
        return;
    }
    log_d("configVideoStreamCallback success");
    cJSON_Delete(bodyJson);
    sos_free(userData);
}

static void refreshVideoIFrameRequest(const char *authCode, VideoConfigParam *configParam){
    cJSON *obj = cJSON_CreateObject();
    cJSON_AddItemToObject(obj, "AuthCode", cJSON_CreateString(authCode));
    cJSON_AddItemToObject(obj, "StreamType", cJSON_CreateString(configParam->streamType));
    char *requestBody = cJSON_PrintUnformatted(obj);
    log_i("refreshVideoIFrameRequest request body msg content = %s", requestBody);

    int result = sos_NwHttpSendRequest(MEDIA_HTTP_SERVER_LOCAL_ADDRESS, 
    MEDIA_HTTP_SERVER_LISTEN_PORT, MEDIA_API_VIDEO_REFRESH_IFRAME, NW_HTTP_METHOD_POST, 
    requestBody, NW_DEFAULT_TOKEN, configParam, 0, refreshIframeCallback);
    sos_free(requestBody);
    cJSON_Delete(obj);
}

//==============================认证、建立相机视频数据订阅通道end============================

static int video_record_start_task(VideoSubscribeTask *task){
    const char *authCode = media_get_auth_code();
    if(is_empty_str(authCode)){
        // MsgObject *msg = (MsgObject *)sos_malloc(sizeof(MsgObject), "MsgObject");
        // msg->msg = task;
        // memset(msg->strMsg, 0, sizeof(msg->strMsg));
        // snprintf(msg->strMsg, sizeof(msg->strMsg), "%s", MEDIA_ACTION_SUBSCRIBE_VIDEO_DATA);
        // media_refresh_auth_code(msg, mediaAuthCodeCallback);
        log_d("video_record_start enter, with empty authCode");
        return -1;
    }
    //发送订阅请求
    subscribeVideoDataRequest(authCode, task);
    //测试代码，用于录像
    if (OUTPUT_VIDEO_DATA_TO_FILE && NULL == videoManager.recordFile){
        videoManager.recordFile = fopen(OUTPUT_VIDEO_DATA_FILE_PATH, "w+");
    }
	return 0;
}

static int video_record_stop_task(VideoSubscribeTask *task){
    const char *authCode = media_get_auth_code();
    log_w("video_record_stop_task for %s", task->taskCode);
    
    cancelSubscribeVideoDataRequest(authCode, task->taskCode);
    pthread_mutex_lock(&videoManager.mutex);
    videoManager.taskList = bctbx_list_remove(videoManager.taskList, task);
    sos_free(task);
    pthread_mutex_unlock(&videoManager.mutex);
    int taskCount = bctbx_list_size(videoManager.taskList);
    log_w("after video_record_subscribe_finish taskCount = %d", taskCount);
    if(taskCount > 0){
        return 0;
    }
    videoManager.taskList = NULL;
    if (OUTPUT_VIDEO_DATA_TO_FILE && NULL != videoManager.recordFile){
        fclose(videoManager.recordFile);
    }
    return 0;
}

static void videoDataCallback(const char *taskCode, char *recvMsg, int msgLength){
    if(NULL == recvMsg){
        return;
    }
    /*
    视频 关键帧及参考帧 
    IFrame      'I' //I֡
    PFrame      'P' //p֡
    音频
    AFrame      'A'
    */
    int frm_type = PFrame; //帧类型
    int code_type = 0; //0 表示 h264 1 表示 h265
    int  pts = 0; //这里pts如果没有给定，库内部会生成。但是有误差
    int ch = 0; //这里一般只录制主码流
    int ret =0;

    //写入缓存 才可以正常录像。
    if(msgLength > 50<<10) //此处方法不准确需要修改。
    {
        frm_type = IFrame;
    }
    ret = STFS_FifoWriteFrame_pts((unsigned char*)recvMsg,msgLength,frm_type,ch,pts,code_type);
    
    log_d("videoDataCallback length = %d ret %d ", msgLength,ret );
    //    video_record_write_data_to_file(recvMsg, msgLength);
}

static void delaySubscribeCallback(void *timerInstance, void *userData){
    video_record_subscribe_start("Demo-Test", MEDIA_VIDEO_STREAM_MAIN, videoDataCallback);
}

int video_record_load_moudle(){
    videoManager.taskList = NULL;
    videoManager.recordFile = NULL;
    pthread_mutex_init(&videoManager.mutex, NULL);
    //延迟30s去订阅数据,等待认证流程完成
    nw_start_timer(30000, NW_FALSE, delaySubscribeCallback, NULL);
    return 0;
}

/**
 * @desc  开始订阅MediaServer视频服
 * 
 * 主要分为如下几个步骤：
 * 1、获取MS的AUthCode；
 * 2、发送视频服务订阅请求；
 * 3、根据订阅请求返回的ws连接信息，建立WS数据通道
 * 4、从ws connect回调接口中获取视频数据，进行业务数据处理
 * */
int video_record_subscribe_start(const char *tag, const char *streamType, MediaDataCallback dataCallback){
    int taskSize = bctbx_list_size(videoManager.taskList);
    //校验当前订阅连接数是否达到最大值
    if(taskSize > MEDIA_VIDEO_DATA_MAX_SUBSCRIBER){
        log_e("subscribeVideoData task already over max limit, current taskSize = %d", taskSize);
        return -2;
    }
    log_d("video_record_subscribe_start for %s", tag);
    //看是否已存在相同的订阅任务
    VideoSubscribeTask *task = findVideoTaskByTag(tag);
    if(NULL != task){
        log_d("video_record_subscribe_start for %s, already exsit", tag);
        pthread_mutex_lock(&videoManager.mutex);
        task->dataCallback = dataCallback;
        pthread_mutex_unlock(&videoManager.mutex);
        return 0;
    }
    //生成订阅任务
    task = (VideoSubscribeTask *)sos_malloc(sizeof(VideoSubscribeTask), "VideoSubscribeTask");
    if(NULL == task){
        log_e("subscribeVideoData gennerate task failed !!!");
        return -1;
    }
    pthread_mutex_lock(&videoManager.mutex);
    memset(task->tag, 0, sizeof(task->tag));
    snprintf(task->tag, sizeof(task->tag), "%s", tag);
    memset(task->streamType, 0, sizeof(task->streamType));
    snprintf(task->streamType, sizeof(task->streamType), "%s", streamType);
    task->dataCallback = dataCallback;
    videoManager.taskList = bctbx_list_append(videoManager.taskList, task);
    pthread_mutex_unlock(&videoManager.mutex);

    int ret = video_record_start_task(task);
    if(-1 == ret){
        pthread_mutex_lock(&videoManager.mutex);
        videoManager.taskList = bctbx_list_remove(videoManager.taskList, task);
        sos_free(task);
        pthread_mutex_unlock(&videoManager.mutex);
    }
    return ret;
}

int video_record_subscribe_finish(const char *tag){
    const char *authCode = media_get_auth_code();
    if(is_empty_str(authCode)){
        log_d("video_record_subscribe_finish without authCode for %s", tag);
        return -1;
    }
    VideoSubscribeTask *task = findVideoTaskByTag(tag);
    if(NULL == task){
        log_d("video_record_subscribe_finish without task for %s", tag);
        return 0;
    }
    int ret = video_record_stop_task(task);
    return ret;
}

int video_record_config_stream_param(const char *streamType, int definition, int bitRate, int frameRate){
    VideoConfigParam *configParam = (VideoConfigParam *)calloc(1, sizeof(VideoConfigParam));
    if(NULL == configParam){
        return -1;
    }
    snprintf(configParam->streamType, sizeof(configParam->streamType), "%s", streamType);
    configParam->definition = definition;
    configParam->bitRate = bitRate;
    configParam->frameRate = frameRate;

    const char *authCode = media_get_auth_code();
    if(is_empty_str(authCode)){
        MsgObject *msg = (MsgObject *)sos_malloc(sizeof(MsgObject), "MsgObject");
        msg->msg = configParam;
        memset(msg->strMsg, 0, sizeof(msg->strMsg));
        snprintf(msg->strMsg, sizeof(msg->strMsg), "%s", MEDIA_ACTION_CONFIG_VIDEO_STREAM);
        media_refresh_auth_code(msg, mediaAuthCodeCallback);
    }else{
        configVideoStreamRequest(authCode, configParam);
    }
	return 0;
}

int video_record_release(){
    if (NULL != videoManager.recordFile){
        fclose(videoManager.recordFile);
        videoManager.recordFile = NULL;
    }
    VideoSubscribeTask *temp = NULL;
    bctbx_list_t *elem = videoManager.taskList;
    for ( ; NULL != elem; elem = videoManager.taskList){
        temp = (VideoSubscribeTask *)elem->data;
        if(NULL == temp){
            continue;
        }
        video_record_stop_task(temp);
    }
    pthread_mutex_destroy(&videoManager.mutex);
    return 0;
}

void video_record_write_data_to_file(char *recvMsg, int msgLength){
    if (NULL == videoManager.recordFile){
        return;
    }
    size_t result = fwrite(recvMsg, 1, msgLength, videoManager.recordFile);
    fflush(videoManager.recordFile);
}